import Config from '../config';
import Services from './services';
import Util from './util';

var _ = require('lodash');
var contextMatcher = require('http-proxy-middleware/lib/context-matcher');
var PathRewriter = require('../polyfill/path-rewriter');
// var PathRewriter = require('http-proxy-middleware/lib/path-rewriter');
var Router = require('http-proxy-middleware/lib/router');
var configFactory = require('http-proxy-middleware/lib/config-factory');

class Proxy {

    static map(req) {
        _.each(Config.proxy, (r, i) => {
            let inst = Services.getInstance('proxy', i, this, [i, r]);
            if (inst) {
                inst.invoke(req);
            }
        });
    }

    static encode(req) {
        let options = this.getProxyOptions({
            url: req.url
        });
        if (options) {
            if (options.contentType) {
                req.contentType = options.contentType;
            }
            let hs = Util.buildArray(options.handlers);
            if (hs && req.data != undefined) {
                hs.forEach(h => {
                    req.data = this.Handlers[h].encode(req.data);
                    req._proxy_decode = true;
                });
            }
        }
    }

    static decode(xhr, result) {
        if (xhr && xhr.requestParameters._proxy_decode === true) {
            let hs = this.getHandlers({
                url: xhr.requestParameters.url
            });
            if (hs && !_.isEmpty(result.responseText)) {
                let data = result.responseText;
                [...hs].reverse().forEach(h => {
                    data = this.Handlers[h].decode(data);
                });
                Util.extend(result, {
                    responseText: data,
                    response: data
                });
            }
        }
    }

    static getProxyOptions(req) {
        let result;
        _.each(Config.proxy, (r, i) => {
            let inst = Services.getInstance('proxy', i, this, [i, r]);
            if (inst) {
                if (inst.shouldProxy(inst.config.context, req)) {
                    if (inst.proxyOptions) {
                        result = inst.proxyOptions;
                        return false;
                    }
                }
            }
        });
        return result;
    }

    static getHandlers(req) {
        let options = this.getProxyOptions(req);
        if (options && options.handlers) {
            return Util.buildArray(options.handlers);
        }
        return [];
    }

    static get Handlers() {
        return {
            'xml-json': {
                encode(json) {
                    return Util.xmlFromJSON(json);
                },
                decode(xml) {
                    return Util.xmlToJSON(xml);
                }
            },
            'base64': {
                encode(value) {
                    return Util.base64Encode(value);
                },
                decode(value) {
                    return Util.base64Decode(value);
                }
            },
            'aes': {
                encode(value) {
                    return Util.encryptAES(value);
                },
                decode(value) {
                    return Util.decryptAES(value);
                }
            },
            'rsa-json': {
                encode(value) {
                    return Util.encryptRSA(value);
                },
                decode(value) {
                    return value;
                }
            },
            'json': {
                encode(value) {
                    return JSON.stringify(value);
                },
                decode(value) {
                    return JSON.parse(value);
                }
            }
        };
    }

    constructor(context, opts) {
        this.config = configFactory.createConfig(context, opts);
        this.proxyOptions = this.config.options;
        this.pathRewriter = PathRewriter.create(this.proxyOptions.pathRewrite);
    }

    invoke(req) {
        if (this.shouldProxy(this.config.context, req)) {
            this.prepareProxyRequest(req);
        }
    }

    shouldProxy(context, req) {
        var path = (req.originalUrl || req.url);
        return contextMatcher.match(context, path, req);
    }

    prepareProxyRequest(req) {
        // https://github.com/chimurai/http-proxy-middleware/issues/17
        // https://github.com/chimurai/http-proxy-middleware/issues/94
        req.url = (req.originalUrl || req.url);

        var newProxyOptions = _.assign({}, this.proxyOptions);

        // Apply in order:
        // 1. option.router
        // 2. option.pathRewrite
        this.__applyRouter(req, newProxyOptions);
        this.__applyPathRewrite(req, this.pathRewriter, newProxyOptions);

        return newProxyOptions;
    }

    __applyRouter(req, options) {
        var newTarget;

        if (options.router) {
            newTarget = Router.getTarget(req, options);

            if (newTarget) {
                //logger.debug('[HPM] Router new target: %s -> "%s"', options.target, newTarget);
                options.target = newTarget;
            }
        }
    }

    // rewrite path
    __applyPathRewrite(req, pathRewriter, options) {
        let path = req.url;
        if (pathRewriter) {
            path = pathRewriter(req.url, req);
        }
        if (typeof path === 'string') {
            req.url = Util.combineUrl(options.target, path);
        } else {
            console.log("重写错误");
        }
    }

}

export default Proxy;